package com.authority.service.impl;

import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.authority.common.AuthConstants;
import com.authority.common.StatusEnum;
import com.authority.dao.AuthActionDAO;
import com.authority.dao.AuthMenuActionDAO;
import com.authority.dao.AuthMenuDAO;
import com.authority.dao.AuthRoleDAO;
import com.authority.dao.AuthRoleMenuDAO;
import com.authority.dao.AuthRoleTypeDAO;
import com.authority.dao.AuthUserDAO;
import com.authority.entry.AuthActionDO;
import com.authority.entry.AuthMenuActionDO;
import com.authority.entry.AuthMenuDO;
import com.authority.entry.AuthRoleDO;
import com.authority.entry.AuthRoleMenuDO;
import com.authority.entry.AuthRoleTypeDO;
import com.authority.query.AuthRoleQuery;
import com.authority.result.PageResult;
import com.authority.service.AuthMenuService;
import com.authority.service.AuthRoleService;
import com.authority.tree.Node;
import com.authority.tree.NodeUtils;
import com.authority.utils.CommonUtils;
import com.authority.utils.CommonUtils.CallBack;
import com.authority.vo.AuthRoleVO;
import com.authority.vo.IdNameVO;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;

@Service("AuthRoleService")
@Transactional(rollbackFor = Exception.class)
public class AuthRoleServiceImpl implements AuthRoleService {

	@Resource
	private AuthRoleDAO authRoleDAO;

	@Resource
	private AuthRoleTypeDAO authRoleTypeDAO;

	@Resource
	private AuthRoleMenuDAO authRoleMenuDAO;

	@Resource
	private AuthMenuDAO authMenuDAO;

	@Resource
	private AuthMenuService authMenuService;

	@Resource
	private AuthActionDAO authActionDAO;

	@Resource
	private AuthMenuActionDAO authMenuActionDAO;

	@Resource
	private AuthUserDAO userDAO;

	@Override
	public PageResult<AuthRoleVO> listAuthRole(AuthRoleQuery query) throws Exception {

		List<AuthRoleVO> list = CommonUtils.dbToVo(authRoleDAO.queryAuthRole(query), AuthRoleVO.class,
				new CallBack<AuthRoleDO, AuthRoleVO>() {
					@Override
					public void execute(AuthRoleDO db, AuthRoleVO vo) {
						vo.setTypeName(authRoleTypeDAO.queryTypeNameById(db.getType())); // 角色类型名称
						vo.setOperator(userDAO.queryUserNameById(db.getOperatorId())); // 操作人
						vo.setType(db.getType().intValue());
					}
				});
		return PageResult.create(list, authRoleDAO.queryAuthRoleCount(query), query.getIndex(), query.getNum());
	}

	@Override
	public void saveAuthRole(AuthRoleDO db, List<AuthRoleMenuDO> list) throws Exception {

		// 检查编码是否重复
		if (authRoleDAO.queryAuthRoleByCodeAndSysId(db.getCode(), db.getSysId()) != null) {
			throw new Exception("角色编码已存在");
		}

		// 添加角色信息
		Long roleId = authRoleDAO.insertAuthRole(db);
		if (roleId > 0)
			roleId = db.getId();

		// 添加角色权限列表
		if (!CollectionUtils.isEmpty(list)) {
			List<AuthRoleMenuDO> newList = Lists.newArrayList();
			for (AuthRoleMenuDO rmdb : list) {
				rmdb.setRoleId(roleId);
				newList.add(rmdb);
			}

			authRoleMenuDAO.batchInsertAuthRoleMenu(newList);
		}
	}

	@Override
	public void updateAuthRole(AuthRoleDO db, List<AuthRoleMenuDO> list) throws Exception {

		// 更新角色信息
		authRoleDAO.updateAuthRole(db);

		// 删除角色的所有权限
		authRoleMenuDAO.batchDeleteAuthRoleMenuByRoleIdAndSysId(db.getId(), db.getSysId());

		// 重新添加角色权限列表
		if (!CollectionUtils.isEmpty(list)) {
			authRoleMenuDAO.batchInsertAuthRoleMenu(list);
		}
	}

	@Override
	public AuthRoleDO getAuthRoleById(Long id) {

		return authRoleDAO.queryAuthRoleById(id);
	}

	@Override
	public List<AuthMenuDO> listAuthMenuByRoleIdAndSysId(Long roleId, Long sysId) {

		List<AuthMenuDO> list = Lists.newArrayList();

		for (AuthRoleMenuDO db : authRoleMenuDAO.queryAuthRoleMenuByRoleIdAndSysId(roleId, sysId)) {
			list.add(authMenuDAO.queryAuthMenuById(db.getMenuId()));
		}

		return list;
	}

	/**
	 * 获取所有节点(菜单+功能)
	 * 
	 * @param sysId
	 * @return
	 */
	private List<Node> getAllNode(Long sysId) {

		// 获取所有菜单并都设为非叶子节点
		List<AuthMenuDO> mList = authMenuService.listAuthMenuBySysId(sysId);
		for (AuthMenuDO mdb : mList) {
			mdb.setIsLeaf(StatusEnum.Disable.getIndex());
		}

		// 转换菜单为节点
		List<Node> nodes = NodeUtils.menuToNode(mList);

		// 转换功能为节点
		Multimap<Long, AuthMenuActionDO> maMap = maListToMap(authMenuActionDAO.queryAuthMenuActionBySysId(sysId));
		Map<Long, AuthActionDO> aMap = aListToMap(authActionDAO.queryAuthActionBySysId(sysId));

		int index = 1; // 顺序号索引
		for (AuthMenuDO mdb : mList) {
			Collection<AuthMenuActionDO> maList = maMap.get(mdb.getId());
			if (!CollectionUtils.isEmpty(maList)) {
				for (AuthMenuActionDO madb : maList) {
					AuthActionDO adb = aMap.get(madb.getActionId());
					nodes.add(NodeUtils.actionToNode(adb, madb.getMenuId(), index++));
				}
			}
		}

		return nodes;
	}

	@Override
	public Node treeUncheckedMenu(Long sysId) {

		return NodeUtils.sortChildren(NodeUtils.generateNodeTree(getAllNode(sysId), AuthConstants.ROOT));
	}

	@Override
	public Node treeCheckedMenu(Long roleId, Long sysId) {

		// 获取所有节点
		List<Node> nodes = getAllNode(sysId);

		Map<String, Node> map = NodeUtils.generateNodeMap(nodes);

		// 设置已选中的节点
		List<AuthRoleMenuDO> rmList = authRoleMenuDAO.queryAuthRoleMenuByRoleIdAndSysId(roleId, sysId);
		for (AuthRoleMenuDO rm : rmList) {
			Long menuId = rm.getMenuId();
			map = NodeUtils.checkedNode(map, menuId.toString());

			String actionIds = rm.getActionIds();
			if (StringUtils.isNotEmpty(actionIds)) {
				for (String actionId : actionIds.split(",")) {
					map = NodeUtils.checkedNode(map, menuId + "_" + actionId);
				}
			}
		}

		// 生成菜单树
		return NodeUtils.sortChildren(NodeUtils.generateNodeTree(map, AuthConstants.ROOT));
	}

	@Override
	public List<IdNameVO> listAuthRoleIdName(Long sysId) {
		AuthRoleQuery query = new AuthRoleQuery();
		query.setSysId(sysId);
		query.setIsDownload(true);

		List<AuthRoleDO> list = authRoleDAO.queryAuthRole(query);
		List<IdNameVO> in = Lists.newArrayList();

		if (!CollectionUtils.isEmpty(list)) {
			for (AuthRoleDO db : list) {
				in.add(new IdNameVO(db.getId(), db.getName()));
			}
		}
		return in;
	}

	@Override
	public Map<String, List<IdNameVO>> listAuthRoleGroupByType(Long sysId) {

		Map<String, List<IdNameVO>> map = Maps.newHashMap();
		List<AuthRoleTypeDO> typeList = authRoleTypeDAO.queryAuthRoleTypeBySysId(sysId);

		if (!CollectionUtils.isEmpty(typeList)) {
			for (AuthRoleTypeDO rtdb : typeList) {

				List<AuthRoleDO> roleList = authRoleDAO.queryAuthRoleByType(rtdb.getId());

				List<IdNameVO> in = Lists.newArrayList();
				if (!CollectionUtils.isEmpty(roleList)) {
					for (AuthRoleDO db : roleList) {
						in.add(new IdNameVO(db.getId(), db.getName()));
					}
				}

				map.put(rtdb.getName(), in);
			}
		}
		return map;
	}

	private Multimap<Long, AuthMenuActionDO> maListToMap(List<AuthMenuActionDO> sourceList) {

		Multimap<Long, AuthMenuActionDO> map = HashMultimap.create();
		for (AuthMenuActionDO madb : sourceList) {
			map.put(madb.getMenuId(), madb);
		}

		return map;
	}

	private Map<Long, AuthActionDO> aListToMap(List<AuthActionDO> sourceList) {

		Map<Long, AuthActionDO> map = Maps.newHashMap();

		for (AuthActionDO adb : sourceList) {
			map.put(adb.getId(), adb);
		}

		return map;
	}

	
	@Override
	public List<AuthRoleVO> getRoleList(AuthRoleQuery query) throws Exception {
		List<AuthRoleVO> list = CommonUtils.dbToVo(authRoleDAO.queryAuthRole(query), AuthRoleVO.class,
				new CallBack<AuthRoleDO, AuthRoleVO>() {
					@Override
					public void execute(AuthRoleDO db, AuthRoleVO vo) {
						vo.setTypeName(authRoleTypeDAO.queryTypeNameById(db.getType())); // 角色类型名称
						vo.setOperator(userDAO.queryUserNameById(db.getOperatorId())); // 操作人
						vo.setType(db.getType().intValue());
					}
				});
		return list;
	}
}
